package wsi.resolve;

import java.io.File;
import java.util.Map.Entry;

import org.rygh.core.html.Html;
import org.rygh.core.html.UHtml;
import org.rygh.core.html.table.HtmlTable;
import org.rygh.core.html.table.TableData;
import org.rygh.core.html.table.TableTuple;
import org.rygh.core.util.UIo;
import org.rygh.core.util.UTime;
import org.rygh.core.util.counter.CounterTuple;
import org.rygh.core.util.counter.SimpleCounter;
import org.rygh.core.xml.UXml;
import org.rygh.core.xml.XmlLeaf;
import org.rygh.core.xml.XmlObject;
import org.rygh.core.xml.XmlPath;
import org.rygh.core.xml.XmlTree;
import org.rygh.core.xml.xsl.XslForEach;
import org.rygh.core.xml.xsl.XslIf;
import org.rygh.core.xml.xsl.XslLeaf;
import org.rygh.core.xml.xsl.XslObject;
import org.rygh.core.xml.xsl.XslTemplate;
import org.rygh.core.xml.xsl.XslTree;
import org.rygh.core.xml.xsl.XslValueOf;
import org.rygh.semantic.model.ex.FactSet;
import org.rygh.semantic.model.rdf.Fact;
import org.rygh.semantic.model.rdf.Prefix;

import wsi.dataguide.DataGuide;
import wsi.dataguide.tree.WSXmlLeaf;
import wsi.function.DirManager;
import wsi.function.Function;
import wsi.function.Variable;
import wsi.fusion.FunctionalityFuser;
import wsi.fusion.VariableManager;
import wsi.match.MatchProperty;
import wsi.match.SubtreeController;

public class FunctionDefiner {

	private final XmlTree xml = new XmlTree();
	private final XslTree xsl = new XslTree();

	private final DirManager dir;
	private final SubtreeController subtreeController;
	private final Resolver resolveManager;
	private final MatchProperty matchProperty;
	private final Function function;
	private final DataGuide dataGuide;

	public FunctionDefiner(DirManager dir, SubtreeController subtreeController,
			Resolver resolveManager, MatchProperty matchProperty) {
		this.dir = dir;
		this.function = dir.getFunction();
		this.dataGuide = dir.getDataGuide();

		this.subtreeController = subtreeController;
		this.resolveManager = resolveManager;
		this.matchProperty = matchProperty;
	}

	private void appendVariable(XslLeaf parent, Variable variable,
			XmlPath xpath, XmlPath relativePath) {
		XslObject obj = new XslObject("ITEM");
		obj.addAttribute("ANGIE-VAR", variable.getValue());

		XslLeaf child = new XslLeaf(parent, obj);
		XslValueOf valueof = new XslValueOf(child);

		XmlPath xpath2;

		if (xpath.presumesWithoutIndex(relativePath))
			xpath2 = new XmlPath(xpath.getRelativePathsTo(relativePath)
					.stripIndex());
		else
			xpath2 = new XmlPath(xpath.stripIndex());

		valueof.setSelect(xpath2.toLocalNameString());
	}

	public void generate() {
		generateXml();
		generateXsl(matchProperty.isExistsMatch());
	}

	private void generateFacts(XmlLeaf parent, SimpleCounter<String> counter,
			String code) {
		XmlObject triple_obj;
		FactSet group = new FactSet();

		for (CounterTuple<String> obj : counter.valueSet()) {
			FactItem item = (FactItem) obj.getValue();

			group.add(item.getFactGroup());
		}

		for (Fact fact : group) {
			triple_obj = new XmlObject("triple");
			triple_obj.addAttribute("code", code);
			triple_obj.addAttribute("subject", fact.getSubject().getValue());
			triple_obj.addAttribute("object", fact.getObject().getValue());
			triple_obj
					.addAttribute("predicate", fact.getPredicate().getValue());

			new XmlLeaf(parent, triple_obj);
		}
	}

	private void generateXml() {
		XmlLeaf parent, child;
		XmlObject obj;

		obj = new XmlObject("ws");
		obj.addAttribute("date", UTime.toTodayString());
		parent = new XmlLeaf(xml.getRoot(), obj);

		obj = new XmlObject("url");
		obj.addAttribute("path", function.getUrl().toString());
		child = new XmlLeaf(parent, obj);

		obj = new XmlObject("parameter");
		if (function.getConvert() != null)
			obj.addAttribute("seperator", function.getConvert());

		new XmlLeaf(child, obj);

		child = new XmlLeaf(parent, new XmlObject("prefixes"));

		for (Entry<String, String> e : Prefix.DEFAULT_PREFIX.getMap()
				.entrySet()) {
			obj = new XmlObject("prefix", e.getValue());
			obj.addAttribute("name", e.getKey());
			new XmlLeaf(child, obj);
		}

		obj = new XmlObject("transformation");
		obj.setValue(String.format("%s.xsl", function.getName()));
		new XmlLeaf(parent, obj);

		obj = new XmlObject("template");
		child = new XmlLeaf(parent, obj);

		generateFacts(child, resolveManager.getFactItemCounter(), "SUB_MATCH");
		generateFacts(child, resolveManager.getTmpFactItemCounter(), "SUB_UN");
	}

	private void generateXsl(boolean isExistsMatch) {
		XslForEach foreach;
		XslLeaf parent;
		XslIf xslif;
		Variable var;

		parent = new XslTemplate(xsl.getConcreteRoot());
		parent = new XslLeaf(parent, new XslObject("RESULT"));

		VariableManager variableManager = resolveManager.getVariableManager();

		WSXmlLeaf dg_root = dataGuide.getConcreteRoot();
		WSXmlLeaf common = subtreeController.getCommonRoot();
		WSXmlLeaf prevNode, relativeRootNode;

		XmlPath xpath, currentRelPath;

		boolean hasSupNodes = dataGuide.hasSupNodes(common);

		if (hasSupNodes)
			prevNode = relativeRootNode = dg_root;
		else
			prevNode = relativeRootNode = common;

		currentRelPath = relativeRootNode.getXpath();
		xpath = currentRelPath.getRelativePathsTo(new XmlPath("/"));

		foreach = new XslForEach(parent);
		foreach.setSelect(xpath.toLocalNameString());

		if (isExistsMatch) {
			xslif = new XslIf(foreach);
			xslif.setPositionEquals(matchProperty.getSubtreeIndex());
			parent = new XslLeaf(xslif, new XslObject("RECORD"));
		} else
			parent = new XslLeaf(foreach, new XslObject("RECORD"));

		/**/

		if (hasSupNodes) {
			for (Entry<Variable, WSXmlLeaf> e : variableManager
					.getVarInverseMap().entrySet()) {
				var = e.getKey();
				xpath = e.getValue().getXpath();

				if (!var.getValue().contains("y"))
					continue;

				appendVariable(parent, var, xpath, currentRelPath);
			}

			relativeRootNode = common;
			currentRelPath = common.getXpath();
			xpath = new XmlPath(currentRelPath.getRelativePathsTo(
					prevNode.getXpath()).stripIndex());
			foreach = new XslForEach(parent);
			foreach.setSelect(xpath.toLocalNameString());

			XslObject obj = new XslObject("SIMPLE-ENTITY");
			obj.addAttribute("name", common.getValue().getName());

			parent = new XslLeaf(foreach, obj);
		}

		for (Entry<Variable, WSXmlLeaf> e : variableManager.getVarInverseMap()
				.entrySet()) {
			var = e.getKey();
			xpath = e.getValue().getXpath();

			if (!var.getValue().contains("x"))
				continue;

			appendVariable(parent, var, xpath, currentRelPath);
		}

		if (resolveManager.getFuser() instanceof FunctionalityFuser) {
			for (Entry<Variable, WSXmlLeaf> e : ((FunctionalityFuser) resolveManager
					.getFuser()).getVarEntityNamingLeafMap().entrySet()) {
				var = e.getKey();
				xpath = e.getValue().getXpath();

				appendVariable(parent, var, xpath, currentRelPath);
			}
		}
	}

	public String save() {
		String xmlString = xml.toXmlString();

		UIo.saveFile(
				new File(dir.getFunctionDir(), String.format("%s.xml",
						function.getName())), xmlString);

		UIo.saveFile(
				new File(dir.getFunctionDir(), String.format("%s.xsl",
						function.getName())), xsl.toXmlString());

		// UIo.saveFile(
		// new File(dir.getFunctionDir(), "transformed_results.html"),
		// toTransformedString());

		return xmlString;
	}

	public String toTransformedString() {
		Html html = new Html(function.getService() + "/" + function.getName());

		HtmlTable table;
		TableTuple tuple;
		TableData data;

		data = new TableData();

		tuple = new TableTuple();
		tuple.add("Input", "Transformed Results");
		data.add(tuple);

		File[] files = dir.getXmlFiles();
		String xml, xsl;

		xsl = UIo.readFile(new File(dir.getFunctionDir(), function.getName()
				+ ".xsl"));
		if (xsl == null)
			return null;

		String s;

		for (File file : files) {
			tuple = new TableTuple();

			tuple.add(file.getName());
			xml = UIo.readFile(file);

			s = UXml.transformXslt(xsl, xml);

			if (s == null)
				tuple.add("");
			else
				tuple.add(UHtml.escape(s));

			data.add(tuple);
		}

		table = new HtmlTable("Transformed Results", data, false);
		html.addElement(table);
		return html.toString();
	}

}
